"""
Base TTS engine interface and configuration.

Defines the common interface for all TTS engines.
"""

from abc import ABC, abstractmethod
from dataclasses import dataclass
from typing import Dict, List, Optional, Any, AsyncGenerator
from pathlib import Path
import asyncio

from ...utils.exceptions import MD2PPTError


@dataclass
class VoiceInfo:
    """Information about a TTS voice."""
    voice_id: str
    name: str
    language: str
    gender: str
    locale: str
    sample_rate: int = 24000
    description: Optional[str] = None
    
    def __str__(self) -> str:
        return f"{self.name} ({self.language}, {self.gender})"


@dataclass
class TTSConfig:
    """Configuration for TTS generation."""
    voice_id: str = "zh-CN-XiaoxiaoNeural"
    rate: str = "+0%"  # Speed: -50% to +200%
    volume: str = "+0%"  # Volume: -50% to +200%
    pitch: str = "+0Hz"  # Pitch: -200Hz to +200Hz
    output_format: str = "audio-24khz-48kbitrate-mono-mp3"
    
    def to_dict(self) -> Dict[str, Any]:
        """Convert config to dictionary."""
        return {
            'voice_id': self.voice_id,
            'rate': self.rate,
            'volume': self.volume,
            'pitch': self.pitch,
            'output_format': self.output_format
        }


@dataclass
class TTSRequest:
    """Request for TTS generation."""
    text: str
    config: TTSConfig
    output_path: Optional[str] = None
    metadata: Optional[Dict[str, Any]] = None
    
    def __post_init__(self):
        if self.output_path is None:
            # Generate default output path
            import tempfile
            import hashlib
            text_hash = hashlib.md5(self.text.encode()).hexdigest()[:8]
            self.output_path = f"{tempfile.gettempdir()}/tts_{text_hash}.mp3"


class BaseTTSEngine(ABC):
    """Base class for TTS engines."""
    
    def __init__(self, config: Optional[TTSConfig] = None):
        """Initialize TTS engine."""
        self.default_config = config or TTSConfig()
        self._available_voices: Optional[List[VoiceInfo]] = None
    
    @abstractmethod
    async def synthesize(self, request: TTSRequest) -> str:
        """
        Synthesize speech from text.
        
        Args:
            request: TTS request with text and configuration
            
        Returns:
            Path to generated audio file
            
        Raises:
            TTSError: If synthesis fails
        """
        pass
    
    @abstractmethod
    async def get_available_voices(self) -> List[VoiceInfo]:
        """
        Get list of available voices.
        
        Returns:
            List of available voice information
        """
        pass
    
    async def synthesize_batch(self, requests: List[TTSRequest]) -> List[str]:
        """
        Synthesize multiple texts in batch.
        
        Args:
            requests: List of TTS requests
            
        Returns:
            List of paths to generated audio files
        """
        tasks = [self.synthesize(request) for request in requests]
        return await asyncio.gather(*tasks)
    
    def get_voice_by_language(self, language: str) -> Optional[VoiceInfo]:
        """Get a default voice for the specified language."""
        if self._available_voices is None:
            # Use builtin voices for synchronous access
            self._available_voices = getattr(self, '_builtin_voices', [])
        
        for voice in self._available_voices:
            if voice.language.startswith(language):
                return voice
        return None
    
    def get_voice_by_id(self, voice_id: str) -> Optional[VoiceInfo]:
        """Get voice information by ID."""
        if self._available_voices is None:
            # Use builtin voices for synchronous access
            self._available_voices = getattr(self, '_builtin_voices', [])
        
        for voice in self._available_voices:
            if voice.voice_id == voice_id:
                return voice
        return None
    
    def estimate_duration(self, text: str, config: Optional[TTSConfig] = None) -> float:
        """
        Estimate audio duration for given text.
        
        Args:
            text: Text to estimate
            config: TTS configuration
            
        Returns:
            Estimated duration in seconds
        """
        # Simple estimation: ~150 words per minute for normal speech
        word_count = len(text.split())
        base_duration = (word_count / 150) * 60
        
        # Adjust for rate
        if config and config.rate:
            rate_str = config.rate.replace('+', '').replace('%', '')
            try:
                rate_multiplier = 1 + (float(rate_str) / 100)
                base_duration = base_duration / rate_multiplier
            except ValueError:
                pass  # Use base duration if rate parsing fails
        
        return max(base_duration, 0.5)  # Minimum 0.5 seconds
    
    def validate_config(self, config: TTSConfig) -> bool:
        """
        Validate TTS configuration.
        
        Args:
            config: Configuration to validate
            
        Returns:
            True if valid
            
        Raises:
            TTSConfigError: If configuration is invalid
        """
        # Validate voice ID
        voice = self.get_voice_by_id(config.voice_id)
        if voice is None:
            raise TTSConfigError(f"Unknown voice ID: {config.voice_id}")
        
        # Validate rate format
        if not self._validate_percentage(config.rate):
            raise TTSConfigError(f"Invalid rate format: {config.rate}")
        
        # Validate volume format
        if not self._validate_percentage(config.volume):
            raise TTSConfigError(f"Invalid volume format: {config.volume}")
        
        # Validate pitch format
        if not self._validate_pitch(config.pitch):
            raise TTSConfigError(f"Invalid pitch format: {config.pitch}")
        
        return True
    
    def _validate_percentage(self, value: str) -> bool:
        """Validate percentage format like '+0%', '-50%'."""
        import re
        pattern = r'^[+-]?\d+%$'
        return bool(re.match(pattern, value))
    
    def _validate_pitch(self, value: str) -> bool:
        """Validate pitch format like '+0Hz', '-200Hz'."""
        import re
        pattern = r'^[+-]?\d+Hz$'
        return bool(re.match(pattern, value))
    
    def get_engine_info(self) -> Dict[str, Any]:
        """Get information about this TTS engine."""
        return {
            'name': self.__class__.__name__,
            'default_config': self.default_config.to_dict(),
            'voice_count': len(self._available_voices) if self._available_voices else 0
        }


class TTSError(MD2PPTError):
    """Base exception for TTS errors."""
    pass


class TTSConfigError(TTSError):
    """Raised when TTS configuration is invalid."""
    pass


class TTSEngineError(TTSError):
    """Raised when TTS engine operation fails."""
    pass